package pl.edu.pb.wi.cookbook.controller;

import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import pl.edu.pb.wi.cookbook.entity.Category;
import pl.edu.pb.wi.cookbook.entity.Recipe;
import pl.edu.pb.wi.cookbook.entity.User;
import pl.edu.pb.wi.cookbook.form.NewRecipeForm;
import pl.edu.pb.wi.cookbook.service.CategoryService;
import pl.edu.pb.wi.cookbook.service.RecipeService;
import pl.edu.pb.wi.cookbook.session.UserSession;

import com.googlecode.objectify.Key;

@Controller
@RequestMapping("/user/recipe")
public class UserRecipeController {
    private static final Logger log = Logger
            .getLogger(UserRecipeController.class.getName());

    private static final String REG = "(http|ftp|https):\\/\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&amp;:/~\\+#]*[\\w\\-\\@?^=%&amp;/~\\+#])?";

    private UserSession userSession;
    private RecipeService recipeService;
    private CategoryService categoryService;

    @Autowired
    public UserRecipeController(UserSession userSession,
            RecipeService recipeService, CategoryService categoryService) {
        this.userSession = userSession;
        this.recipeService = recipeService;
        this.categoryService = categoryService;
    }

    @RequestMapping(value = "create", method = RequestMethod.GET)
    public String create(Model model) {
        model.addAttribute("form", new NewRecipeForm());

        return "user/recipe/create";
    }

    @RequestMapping(value = "create", method = RequestMethod.POST)
    public String create(@ModelAttribute("form")
    @Valid
    NewRecipeForm form, BindingResult results, Model model) {
        String returnPath = "user/recipe/create";

        if (form.getImageUrl() != null && !form.getImageUrl().isEmpty()
                && !form.getImageUrl().matches(REG)) {

            results.addError(new FieldError("form", "imageUrl",
                    "To nie jest adres URL!"));
        }

        if (results.hasErrors()) {
            return returnPath;
        }

        try {
            Category cat = categoryService.getCategory(form.getCategory());
            if (cat != null && cat.isCanAddRecipes()) {
                recipeService.createRecipe(userSession.getUser().getId(),
                        form.getCategory(), form.getName(),
                        form.getDescription(), form.getShortDescription(),
                        form.getIngredients(), form.getPrepareTime(),
                        form.getServings(), form.getImageUrl(),
                        form.getIsPrivate());

                model.addAttribute("successMsgs", Arrays
                        .asList(new String[] { "Pomyślnie oddano przepis." }));
            } else {
                model.addAttribute(
                        "errorMsgs",
                        Arrays.asList(new String[] { "Nie można dodawać przepisów do podanej kategorii." }));
            }

            returnPath = "message";
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with NewRecipeForm form. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));

            returnPath = "message";
        }

        return returnPath;
    }

    @RequestMapping("management/list")
    public String managementList(Model model) {
        try {
            model.addAttribute("recipes",
                    recipeService.getUserRecipes(userSession.getUser()));
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with recipe management list. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));

            return "message";
        }

        return "user/recipe/management/list";
    }

    @RequestMapping("list")
    public String list(Model model) {
        try {
            List<Category> categories = recipeService
                    .getCategoriesWithUserRecipes(userSession.getUser());
            Category currentCat = categories.size() > 0 ? categories.get(0)
                    : null;
            List<Recipe> recipes = null;
            if (currentCat != null)
                recipes = recipeService.getUserRecipesFromCategory(
                        userSession.getUser(), currentCat);

            model.addAttribute("currentCat", currentCat);
            model.addAttribute("categories", categories);
            model.addAttribute("recipes", recipes);
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with my recipe list. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));

            return "message";
        }

        return "user/recipe/list";
    }

    @RequestMapping("list/{catId}")
    public String list(@PathVariable("catId")
    Long id, Model model) {
        try {
            Category currentCat = categoryService.getCategory(id);

            if (currentCat != null) {
                List<Category> categories = recipeService
                        .getCategoriesWithUserRecipes(userSession.getUser());
                List<Recipe> recipes = recipeService
                        .getUserRecipesFromCategory(userSession.getUser(),
                                currentCat);

                model.addAttribute("currentCat", currentCat);
                model.addAttribute("categories", categories);
                model.addAttribute("recipes", recipes);
            } else {
                model.addAttribute("errorMsgs", Arrays
                        .asList(new String[] { "Nieprawidłowa kategoria." }));

                return "forward:/user/recipe/list";
            }
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with my recipe list. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));
        }

        return "user/recipe/list";
    }

    @RequestMapping("delete/{id}")
    public String delete(@PathVariable("id")
    Long id, Model model) {
        String returnPath = "forward:/user/recipe/management/list";

        try {
            Recipe rec = recipeService.getRecipe(id);
            if (rec != null) {
                if (rec.getAuthor().getId() == userSession.getUser().getId()) {
                    String name = rec.getName();
                    recipeService.removeRecipe(rec);

                    model.addAttribute("successMsgs", Arrays
                            .asList(new String[] { String.format(
                                    "Przepis '%s' usunięto pomyślnie", name) }));
                } else {
                    model.addAttribute("errorMsgs", Arrays
                            .asList(new String[] { "Nieprawidłowy przepis." }));
                }
            } else {
                model.addAttribute("errorMsgs", Arrays
                        .asList(new String[] { "Nieprawidłowy przepis." }));
            }
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with deleteform form. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));

            returnPath = "message";
        }

        return returnPath;
    }

    @RequestMapping(value = "edit/{id}", method = RequestMethod.GET)
    public String edit(@PathVariable("id")
    long id, Model model) {
        String returnPath = "message";

        try {
            Recipe rec = recipeService.getRecipe(id);

            if (rec != null) {
                if (rec.getAuthor().getId() == userSession.getUser().getId()) {
                    model.addAttribute("id", id);
                    model.addAttribute("form", converRecipeToRecipeForm(rec));

                    returnPath = "user/recipe/edit";
                } else {
                    model.addAttribute("errorMsgs", Arrays
                            .asList(new String[] { "Nieprawidłowy przepis." }));
                }
            } else {
                model.addAttribute("errorMsgs", Arrays
                        .asList(new String[] { "Nieprawidłowy przepis." }));
            }
        } catch (Exception e) {
            log.info(String.format("Problem with editform form. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));
        }

        return returnPath;
    }

    private NewRecipeForm converRecipeToRecipeForm(Recipe rec) {
        NewRecipeForm form = new NewRecipeForm();
        form.setCategory(rec.getCategory().getId());
        form.setImageUrl(rec.getImageUrl());
        form.setIngredients(rec.getIngredients());
        form.setIsPrivate(rec.isPrivate());
        form.setName(rec.getName());
        form.setPrepareTime(rec.getPrepareTime());
        form.setServings(rec.getServings());
        form.setShortDescription(rec.getShortDescription());
        form.setDescription(rec.getDescription());

        return form;
    }

    @RequestMapping(value = "edit/{id}", method = RequestMethod.POST)
    public String edit(@ModelAttribute("form")
    @Valid
    NewRecipeForm form, BindingResult results, Model model, @PathVariable("id")
    Long id) {
        String returnPath = "message";
        model.addAttribute("id", id);

        if (form.getImageUrl() != null && !form.getImageUrl().isEmpty()
                && !form.getImageUrl().matches(REG)) {

            results.addError(new FieldError("form", "imageUrl",
                    "To nie jest adres URL!"));
        }

        if (results.hasErrors()) {
            return "user/recipe/edit";
        }

        try {
            Recipe rec = recipeService.getRecipe(id);

            if (rec != null) {
                if (rec.getAuthor().getId() == userSession.getUser().getId()
                        || userSession.getIsAdmin() == true) {
                    Recipe updatedRec = converRecipeFormToRecipe(form, rec);

                    Category cat = categoryService.getCategory(updatedRec
                            .getCategory());
                    if (cat != null && cat.isCanAddRecipes()) {
                        recipeService.updateRecipe(updatedRec);

                        model.addAttribute("successMsgs", Arrays
                                .asList(new String[] { String.format(
                                        "Zapisano zmiany w przepisie '%s'.",
                                        updatedRec.getName()) }));

                        if (userSession.getIsAdmin())
                            returnPath = String.format(
                                    "forward:/recipe/list/%d", updatedRec
                                            .getCategory().getId());
                        else
                            returnPath = "forward:/user/recipe/management/list";
                    } else {
                        model.addAttribute(
                                "errorMsgs",
                                Arrays.asList(new String[] { "Podano kategorię do której nie można dodawać przepisów." }));
                    }
                } else {
                    model.addAttribute("errorMsgs", Arrays
                            .asList(new String[] { "Nieprawidłowy przepis." }));
                }
            } else {
                model.addAttribute("errorMsgs", Arrays
                        .asList(new String[] { "Nieprawidłowy przepis." }));
            }
        } catch (Exception e) {
            log.info(String.format(
                    "Problem with NewRecipeForm form. Exception: %s",
                    e.getMessage()));

            model.addAttribute(
                    "errorMsgs",
                    Arrays.asList(new String[] { "Wystąpił błąd. Spróbuj ponownie." }));
        }

        return returnPath;
    }

    private Recipe converRecipeFormToRecipe(NewRecipeForm form, Recipe rec) {
        rec.setCategory((new Key<Category>(Category.class, form.getCategory())));
        rec.setAuthor((new Key<User>(User.class, rec.getAuthor().getId())));
        rec.setImageUrl(form.getImageUrl());
        rec.setIngredients(form.getIngredients());
        rec.setPrivate(form.getIsPrivate());
        rec.setName(form.getName());
        rec.setPrepareTime(form.getPrepareTime());
        rec.setServings(form.getServings());
        rec.setShortDescription(form.getShortDescription());
        rec.setDescription(form.getDescription());

        return rec;
    }
}
